#include <stdlib.h>
#include <stdio.h>
#include "Border.h"
#include "PinnedObject.h"
#include "GrenadeObject.h"
#include "Vector2DList.h"
#include "GL/glut.h"
#include "include.h"


#define TEXTURE_FILE "./Images/ground.bmp"
Border::Border() 
{
	borderLine = NULL;
	borderSegments = NULL;
	numVertices = 0;
}

Border::Border(Vector2D * borderLine, int numVertices, TextureManager * textureManager)
{
	this->textureManager = textureManager;
	if(this->textureManager)
	{
		this->textureManager->LoadTexture(TEXTURE_FILE);
		this->textureManager->LoadTexture(STONEWALL_FILE);
	}
	this->borderLine = borderLine;
	this->numVertices = numVertices;
	numSegments = numVertices - 1;
	
	borderSegments = (LineSegment *)malloc(sizeof(LineSegment) * numSegments);
	
	for(int i = 0; i < numSegments; i++)
	{
		borderSegments[i] = LineSegment(borderLine[i], borderLine[i + 1]);
	}
	CalculateLines();
	CalculateSkirt();
	
}
Border::~Border()
{
	if(borderLine)
		delete(borderLine);
	borderLine = NULL;
	if(borderSegments)
		delete(borderSegments);
	borderSegments = NULL;
	
	if(topLine)
		delete(topLine);
	topLine = NULL;
	
	if(bottomLine)
		delete(bottomLine);
	bottomLine = NULL;
	
}
	
bool Border::Collide(Object * object)
{

	if(object->objectId == GRENADE_OBJECT_CLASS)
	{
		GrenadeObject * grenade = (GrenadeObject *)object;
		if(grenade->IsFuseExpired())
		{
			return false;
		}	
	}
	bool groundForce = true;
	if((object->isPinned) || !(object->GetCollideWithGround()))// || (object->velocity.Magnitude() == 0.0))
		return false;
		
	
	bool didCollide = false;
	float maxDistance = -10000.0;
	float minDistance = 1000.0;
	Vector2D finalResult;
	Vector2D finalOrigin;
	LineSegment collideSegment;
	
	//printf("Checking Border Collision\n");
	for(int i = 0; i < numSegments; i++)
	{
		Vector2D result, origin;
		float distance;
		bool thisDidCollide = borderSegments[i].Collide(object, &distance, &result, &origin);
		didCollide |= thisDidCollide;
		if(thisDidCollide)
		{
			//printf("Segment %d collided!\n", i);
			
			if(distance > maxDistance)
			{
				//printf("Better collision at segment %d\n", i);
				collideSegment = borderSegments[i];
				maxDistance = distance;
				minDistance = distance;
				//minError = error;
				finalResult = result;
				finalOrigin = origin;
			}
			//if(distance < minDistance)
			//	minDistance = distance;
		}
		else
		{
			//printf("Segment %d did not collide!\n", i);
		}
	}
	
	//we checked all the edges, now we have to check all the vertices
	if(1)//!didCollide)
	{
		for(int i = 1; i < numVertices; i++)
		{
			Vector2D result, origin;
			float distance;
		
			bool thisDidCollide = VertexCollide(object, i, &distance, &result, &origin);
			didCollide |= thisDidCollide;
			if(thisDidCollide)
			{
				//float sinTheta = borderSegments[i-1].direction.x * borderSegments[i].direction.y - borderSegments[i-1].direction.y * borderSegments[i].direction.x;
				
				if(distance > maxDistance)// || sinTheta < 0)
				{
					//printf("Vertex %d collided!\n", i);
					//groundForce = false;
					collideSegment = borderSegments[i];
					minDistance = distance;
					maxDistance = distance;
					finalResult = result;
					finalOrigin = origin;
					object->collisionPoint = origin;
				}
			}
			else
			{
				//printf("Vertex %d did not collide!\n", i);
			}
			
		}
	}
	
	if(didCollide)
	{
		//printf("max distance = %f\n", maxDistance);
		if(finalResult.Magnitude() == 0.0)
			return true;
		//maxDistance *= 5.0;
		Vector2D translationalAxis = !finalResult;
		Vector2D deltaPosition = translationalAxis * maxDistance;
		
		if(object->objectId != PINNEDOBJECT_CLASS)
		{
			object->position = object->position + deltaPosition;
			//printf("delta position = %f\n", deltaPosition.Magnitude());
			//object->velocity = object->velocity + deltaPosition/object->lastElapsedTime;
		}
		else
		{
			//rotate it back into the clear
			//object->Rotate(finalOrigin, deltaPosition);	
			//printf("Rotate from border collision\n");
			PinnedObject * po = (PinnedObject *)object;
			Vector2D finalEdge[2] = {collideSegment.left, collideSegment.right};
			po->RotateToEdge(finalOrigin - deltaPosition, finalEdge, true);
		}
		object->AddForceObjectSpace(finalResult, finalOrigin, groundForce);
		if(groundForce)
		{
			collideSegment.maxStaticFrictionalForce += object->material->friction;
			object->AddContactPointObjectSpace(finalResult*-1.0,finalOrigin, collideSegment);
		}
	}
	if(didCollide)
	{
		object->isColliding = true;
		object->CollidedWith(NULL, finalResult.Magnitude(), finalOrigin);
	}
	return didCollide;
}
bool Border::VertexCollide(Object * object, int vertexIndex, float * distance, Vector2D * finalForce, Vector2D * origin)
{
	
	if(vertexIndex == 0 || vertexIndex == numVertices)
		return false;
		
	Vector2D vertex = borderLine[vertexIndex];
	float dist = object->position.dist(vertex);
	float maxDistance = (object->boundingRadius);

	if(dist > maxDistance)
		return false;	
	
	Vector2D collision1;
	Vector2D collision2;
	Vector2D force1;
	Vector2D force2;

	if(!borderSegments[vertexIndex].Collide(object, &collision1))
	{
		//printf("Dropped here\n");
		return false;
	}
	if(!borderSegments[vertexIndex - 1].Collide(object, &collision2))
	{
		//printf("Dropped here2\n");
		return false;
	}
	*finalForce = (borderSegments[vertexIndex].normal + borderSegments[vertexIndex - 1].normal) * -0.5;//(force1 + force2) * 0.5;
	if(finalForce->Magnitude() < 0.0001)
		return false;
	Vector2D translationalAxis = !*finalForce;
	
	LineSegment forceLine(vertex, vertex - translationalAxis * 1000);//vertex.dist(object->position));
	
	Vector2D collision3;// = (collision1 + collision2) * 0.5;
	if(!forceLine.Collide(object, &collision3))
	{
		//printf("Dropped here3\n");
		return false;
	}
	
	//printf("vertex = (%f, %f)\n", vertex.x, vertex.y);
	//printf("collision3 = (%f, %f)\n", collision3.x, collision3.y);
	
	*distance = collision3.dist(vertex - object->position);
	//printf("Vertex distance = %f\n", *distance);
	*origin = collision3;// - object->position;
	Vector2D velocityAtPoint1 = object->VelocityAtPoint(*origin);
	float velocityProjection1 = translationalAxis.dot(velocityAtPoint1);

	if(velocityProjection1 < 0)
		return false;
	float velocityChange1 = (-1.2 - object->material->bounciness) * velocityProjection1;

	
	*finalForce = ((velocityChange1 * object->mass * translationalAxis));///object->lastElapsedTime);
		
	return true;
}
bool Border::CheckIntersection(Vector2D leftPoint, Vector2D rightPoint)
{
	LineSegment testLine = LineSegment(leftPoint, rightPoint);
	for(int i=0; i < numSegments; i++)
	{	
		Vector2D intersection;
		if(testLine.Collide(&borderSegments[i], &intersection))
		{
			if(intersection.dist(leftPoint) > 0.00001 && intersection.dist(rightPoint) > 0.00001)
				return true;
		}
	}
	return false;
}
void Border::SnipSkirt(Vector2DList * remainingPoints)
{
	List<Vector2D> * leftPoint = remainingPoints->head;
	bool foundOne = false;
	
	while(leftPoint)
	{
		List<Vector2D> * middlePoint = leftPoint->next;
		if(middlePoint)
		{
			List<Vector2D> * rightPoint = middlePoint->next;
			if(rightPoint)
			{
				Vector2D leftRay = !((*leftPoint->payload) - (*middlePoint->payload));
				Vector2D rightRay = !((*rightPoint->payload) - (*middlePoint->payload));
				float angleDir = leftRay.AngleDirection(rightRay);
				if(angleDir < 0)
				{
					if(!CheckIntersection((*leftPoint->payload), (*rightPoint->payload)))
					{
						foundOne = true;
						skirtTriangles.Add(leftPoint->payload);
						skirtTriangles.Add(middlePoint->payload);
						skirtTriangles.Add(rightPoint->payload);
						remainingPoints->RemoveFromList(middlePoint);
						break;
					}
				}
			}	
		}
		leftPoint = middlePoint;
	}	
	if(foundOne)
	{
		SnipSkirt(remainingPoints);	
	}
}
void Border::CalculateSkirt()
{
	Vector2DList * remainingPoints = new Vector2DList();
	
	for(int i = 0; i < numVertices; i++)
		remainingPoints->Add(&borderLine[i]);
		
	SnipSkirt(remainingPoints);
	//return;
	List<Vector2D> * leftPoint = remainingPoints->head;
	if(leftPoint && leftPoint->next)
	{
		Vector2D lastUp = !((*leftPoint->payload) - (*leftPoint->next->payload));
		lastUp = Vector2D(-lastUp.y, lastUp.x);
		Vector2D * lastPoint = NULL;
		while(leftPoint)
		{
			List<Vector2D> * rightPoint = leftPoint->next;
			if(rightPoint)
			{
				Vector2D up = !((*leftPoint->payload) - (*rightPoint->payload));
				up = Vector2D(-up.y, up.x);
				Vector2D temp = up;
				up = (up + lastUp) * 0.5;
				Vector2D * point = new Vector2D(leftPoint->payload->x - up.x * 5000.0, leftPoint->payload->y - up.y * 5000.0);
				skirtTriangles.Add(leftPoint->payload);
				skirtTriangles.Add(rightPoint->payload);
				skirtTriangles.Add(point);
				
				if(lastPoint)
				{
					skirtTriangles.Add(lastPoint);
					skirtTriangles.Add(leftPoint->payload);
					skirtTriangles.Add(point);
				}
				
				lastPoint = point;
				lastUp = temp;
			}
			leftPoint = rightPoint;
		}
	}
}
void Border::CalculateLines()
{
	topLine = (Vector2D*)malloc(sizeof(Vector2D)*numVertices);
	bottomLine = (Vector2D*)malloc(sizeof(Vector2D)*numVertices);

	for(int i = 0; i < numVertices; i++)
	{
		bool has1 = false;
		Vector2D right;
		Vector2D up1;
		if(i < numVertices-1)
		{
			has1 = true;
			right = borderLine[i+1] - borderLine[i];
			up1 = Vector2D(-right.y, right.x);
			up1 = !up1;
		}
		
		bool has2 = false;
		Vector2D right2;
		Vector2D up2;
		if(i > 0)
		{
			has2 = true;
			right2 = borderLine[i] - borderLine[i-1];
			up2 = Vector2D(-right2.y, right2.x);
			up2 = !up2;
		}
		
		Vector2D myUp;
		if(has1 && has2)
		{
			myUp = (up1 + up2) * 0.5;
		}
		else if(has1)
		{
			myUp = up1;
		}
		else
		{
			myUp = up2;
		}
		
		topLine[i] = borderLine[i] + 5.0 * myUp;
		bottomLine[i] =  borderLine[i] - 5.0 * myUp;
	}
	
	
}

void Border::Draw()
{
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
	glEnable( GL_ALPHA_TEST );
  	glDisable( GL_BLEND);
  	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
  	//glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
	glColor3f(1,1,1);
	
	if(textureManager)
		textureManager->EnableTexture(TEXTURE_FILE);
	glBegin(GL_QUADS);

	float startXTop = 0;
	float startXBottom = 0;

	for(int i = 0; i < numVertices-1; i++)
	{		
		Vector2D point = topLine[i];
		glTexCoord2f(startXTop, 1.0);
		glVertex2f(point.x, point.y);
		
		point = topLine[i+1];
		float lineLength = ((topLine[i] - topLine[i+1]).Magnitude() + (bottomLine[i] - bottomLine[i+1]).Magnitude()) * 0.5;
		startXTop = startXTop + lineLength / 20.0;
		glTexCoord2f(startXTop, 1);
		glVertex2f(point.x, point.y);
		
		float x = startXBottom + lineLength / 20.0;
		point = bottomLine[i+1];
		glTexCoord2f(x, 0.01);
		glVertex2f(point.x, point.y);
		
		point = bottomLine[i];
		glTexCoord2f(startXBottom, 0.01);
		glVertex2f(point.x, point.y);		
		startXBottom = x;
	}
	glEnd();
	
	if(textureManager)
		textureManager->EnableTexture(STONEWALL_FILE);
	glBegin(GL_TRIANGLES);
	glColor3f(1,1,1);
	List<Vector2D> * search = skirtTriangles.head;
	int i = 0;
	Vector2D firstPoint;
	while(search)
	{
	
		Vector2D point = *(search->payload);
		glTexCoord2f(point.x * 0.01, point.y * 0.01);
		glVertex2f(point.x, point.y);
		search = search->next;
		i++;
		
	}
	glEnd();
}
